package zhoug.zui.task;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

import zhoug.common.utils.CollectionUtils;
import zhoug.logging.Logger;
import zhoug.zui.R;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import androidx.core.content.ContextCompat;

/**
 * 任务进度流程图
 * zui_style_ZUITaskFlowView
 *
 * @Author 35574
 * @Date 2021/2/26
 * @Description
 */
public class ZUITaskFlowView extends View {
    private static final String TAG = ">>>ZUITaskFlowView";
    private static boolean debug = false;
    private List<TaskFlow> mData;
    private static final int LEFT = 1;
    private static final int CENTER = 2;
    private static final int RIGHT = 3;

    /**
     * 任务节点圆形的半径
     */
    private float nodeRadius = 15;

    /**
     * 任务节点颜色1
     */
    private int nodeColor1 = Color.GRAY;
    /**
     * 任务节点颜色2
     */
    private int nodeColor2 = Color.RED;

    /**
     * 2个节点之间的连接线
     */
    private float lineLength = 150;

    /**
     * 节点连线和节点圆之间的间隔
     */
    private float lineMarginNode = 10;

    /**
     * 节点连线的宽度
     */
    private float lineWidth = 6;

    /**
     * 虚线间隔
     */
    private float dashGap = 12;
    /**
     * 虚线的长度
     */
    private float dashWidth = 12;

    /**
     * 文字和节点之间的间距
     */
    private float textMarginNode = 15;
    private int gravity;

    private float textSize1 = 42;//最大号字
    private float textSize2 = 36;//中号
    private float textSize3 = 30;//小号

    private int textColor = 0xff333333;
    private boolean textBold = true;
    private boolean debugData = false;
    /**
     * 人员和节点名之间的间隔
     */
    private float executorMarginName = 20;
    private float childNodeMarginV = 10;
    private float childNodeHeight = 0;
    private float iconWidth = 36;

    private Paint mPaint;
    private TextPaint mTextPaint;
    private DashPathEffect mDashPathEffect;
    private Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();

    private List<Node> nodes = new ArrayList<>();
    private List<Node> iconNodes = new ArrayList<>();
    private GestureDetector mGestureDetector;
    private OnNodeClickListener mOnNodeClickListener;

    public static void setDebug(boolean debug) {
        ZUITaskFlowView.debug = debug;
    }

    public ZUITaskFlowView(Context context) {
        this(context, null);
    }

    public ZUITaskFlowView(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.ZUITaskFlowView_theme);
    }

    public ZUITaskFlowView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZUITaskFlowView, defStyleAttr, R.style.ZUITaskFlowView_def);
        textColor = a.getColor(R.styleable.ZUITaskFlowView_zui_taskFlowView_textColor, 0xff333333);
        textSize1 = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_textSize1, 42);
        textSize2 = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_textSize2, 36);
        textSize3 = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_textSize3, 30);
        nodeColor1 = a.getColor(R.styleable.ZUITaskFlowView_zui_taskFlowView_nodeColor1, Color.GRAY);
        nodeColor2 = a.getColor(R.styleable.ZUITaskFlowView_zui_taskFlowView_nodeColor2, Color.RED);
        nodeRadius = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_nodeRadius, 15);
        lineLength = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_lineLength, 150);
        lineMarginNode = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_lineMarginNode, 10);
        lineWidth = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_lineWidth, 6);
        dashGap = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_dashGap, 12);
        dashWidth = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_dashWidth, 12);
        iconWidth = a.getDimension(R.styleable.ZUITaskFlowView_zui_taskFlowView_iconWidth, 36);
        textBold = a.getBoolean(R.styleable.ZUITaskFlowView_zui_taskFlowView_textBold, true);
        debugData = a.getBoolean(R.styleable.ZUITaskFlowView_zui_debug, false);
        gravity = a.getInt(R.styleable.ZUITaskFlowView_zui_taskFlowView_gravity, CENTER);

        a.recycle();


        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setAntiAlias(true);

        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setAntiAlias(true);

        mDashPathEffect = new DashPathEffect(new float[]{dashWidth, dashGap}, 0);
        mGestureDetector = new GestureDetector(context, onGestureListener);

        if (debugData) {
            List<TaskFlow> taskFlows = Arrays.asList(
                    new TaskFlow("2020-01-01", "发布", "国家管理员", true),
                    new TaskFlow("2020-01-01", "审核", "张三", true),
                    new TaskFlow("2020-01-01", "派发", "李四", true),
                    new TaskFlow("2020-01-01", "检查", null, false),
                    new TaskFlow("2020-01-01", "任务结束", null, false),
                    new TaskFlow("2020-01-01", "归档", null, false)
            );
            List<TaskFlow> childNodes = new ArrayList<>();
            childNodes.add(new TaskFlow("转交", null, "(1)"));
            childNodes.add(new TaskFlow("限期整改", "李四", null));
            childNodes.add(new TaskFlow("委办", "张三", "(2/3)"));
            childNodes.add(new TaskFlow("处罚", null, "(2)"));
//        childNodes.add(new TaskFlowNode("处罚2", null, "(2)"));
//        childNodes.add(new TaskFlowNode("处罚2", null, "(2)"));
            taskFlows.get(3).setChild(childNodes);
            taskFlows.get(1).setIcon(ContextCompat.getDrawable(context, R.drawable.zui_search));
            setData(taskFlows);
        }

    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mData == null || mData.size() == 0) {
            return;
        }

        float nodeX = getWidth() >> 1;//节点圆心坐标x
        if (gravity == LEFT) {
            mTextPaint.setFakeBoldText(false);
            mTextPaint.setTextSize(textSize3);
            float timeTextLength = mTextPaint.measureText("2021-09-29");
            nodeX=getPaddingStart()+timeTextLength+textMarginNode+nodeRadius;

        }

        float nodeY = nodeRadius + getPaddingTop();//节点圆心坐标y

        //线的坐标
        float startY = 0;
        float endY = 0;
        int size = mData.size();
        TaskFlow node;//节点
        TaskFlow nextNode;//下一个节点
        for (int index = 0; index < size; index++) {
            float nextNodeY = 0;//下一个节点Y
            node = mData.get(index);
            drawNode(canvas, nodeX, nodeY, node);
            startY = nodeY + nodeRadius + lineMarginNode;
            //画子节点
            List<TaskFlow> child = node.getChild();
            if (child != null && child.size() > 0) {
                drawChildNode(canvas, nodeX, nodeY, node);
                startY += lineLength;
                nextNodeY = startY + nodeRadius + lineMarginNode + childNodeHeight;
            }
            if (nextNodeY == 0) {
                nextNodeY = nodeY + nodeRadius * 2 + lineMarginNode * 2 + lineLength;
            }

            //绘制节点连线
            if (index < size - 1) {
                nextNode = mData.get(index + 1);
                endY = nextNodeY - nodeRadius - lineMarginNode;
                drawNodeLine(canvas, nodeX, startY, nodeX, endY, !nextNode.isComplete());
            }
            nodeY = nextNodeY;
        }


    }


    /**
     * 绘制节点
     *
     * @param canvas
     * @param nodeX
     * @param nodeY
     * @param taskFlow
     */
    private void drawNode(Canvas canvas, float nodeX, float nodeY, TaskFlow taskFlow) {
        //画节点圆
        drawNodeCircle(canvas, nodeX, nodeY, !taskFlow.isComplete());
        RectF rect = getNode(taskFlow).getRect();
        //绘制左边时间
        String time = taskFlow.getTime();
        rect.left = nodeX - nodeRadius;
        rect.top = nodeY - nodeRadius;
        rect.right = nodeX + nodeRadius;
        rect.bottom = nodeY + nodeRadius;
        if (time != null) {
            mTextPaint.setFakeBoldText(false);
            mTextPaint.setTextSize(textSize3);
            mTextPaint.getFontMetrics(mFontMetrics);
            mTextPaint.setColor(textColor);
            float x = nodeX - textMarginNode - nodeRadius;
            float y = nodeY - (mFontMetrics.descent + mFontMetrics.ascent) / 2;
            mTextPaint.setTextAlign(Paint.Align.RIGHT);
            canvas.drawText(time, x, y, mTextPaint);
            float textWidth = mTextPaint.measureText(time);
            rect.left = x - textWidth;
            float textH = mFontMetrics.bottom - mFontMetrics.top;
            float top = nodeY - textH / 2;
            float bottom = nodeY + textH / 2;
            if (top < rect.top) {
                rect.top = top;
            }
            if (bottom > rect.bottom) {
                rect.bottom = bottom;
            }

        }
        //绘制右边的节点名
        float textX = 0;
        float textY = 0;
        String name = taskFlow.getName();
        float textWidth = 0;
        if (name != null) {
            mTextPaint.setTextSize(textSize1);
            mTextPaint.setFakeBoldText(textBold);
            mTextPaint.getFontMetrics(mFontMetrics);

            mTextPaint.setColor(textColor);
            textX = nodeX + textMarginNode + nodeRadius;
            textY = nodeY - (mFontMetrics.descent + mFontMetrics.ascent) / 2;
            mTextPaint.setTextAlign(Paint.Align.LEFT);
            canvas.drawText(name, textX, textY, mTextPaint);

            textWidth = mTextPaint.measureText(name);
            rect.right = textX + textWidth;
        }

        //绘制人名
        String executor = taskFlow.getExecutor();
        if (executor != null) {
            mTextPaint.setTextSize(textSize2);
            mTextPaint.setFakeBoldText(false);
            mTextPaint.getFontMetrics(mFontMetrics);
            mTextPaint.setColor(textColor);
            if (textX == 0) {
                textX = nodeX + executorMarginName + nodeRadius;
            } else {
                textX += textWidth + executorMarginName;
            }
            textY = nodeY - (mFontMetrics.descent + mFontMetrics.ascent) / 2;
            mTextPaint.setTextAlign(Paint.Align.LEFT);
            canvas.drawText(executor, textX, textY, mTextPaint);
            textWidth = mTextPaint.measureText(executor);
            rect.right = textX + textWidth;
        }
        Drawable icon = taskFlow.getIcon();
        if (icon != null) {
            float ratio = iconWidth / icon.getIntrinsicWidth();
            int iconHeight = (int) (icon.getIntrinsicHeight() * ratio);
            icon.setBounds(0, 0, (int) iconWidth, iconHeight);
            canvas.save();
            float tanX;
            float tanY = nodeY - (iconHeight >> 1);
            if (textX == 0) {
                tanX = nodeX + executorMarginName + nodeRadius;
            } else {
                tanX = textX + textWidth + executorMarginName;
            }
            canvas.translate(tanX, tanY);
            icon.draw(canvas);
            canvas.restore();
            RectF iconRect = getIconNode(taskFlow).getRect();
            iconRect.left = tanX;
            //+15 为了方便点击事件
            iconRect.top = tanY + 15;
            iconRect.right = iconRect.left + iconWidth + 15;
            iconRect.bottom = iconRect.top + iconHeight + 15;


        }

        if (debug) {
            Logger.d(TAG, "drawNode:rect=" + rect);
        }
    }

    /**
     * 绘制子节点
     *
     * @param canvas
     * @param nodeX    节点的x
     * @param nodeY    节点的y
     * @param taskFlow 节点
     */
    private void drawChildNode(Canvas canvas, float nodeX, float nodeY, TaskFlow taskFlow) {
        List<TaskFlow> child = taskFlow.getChild();
        if (child != null && child.size() > 0) {
            float lineW = getWidth() * 1.0f / (child.size() + 2) - nodeRadius - lineMarginNode;
            if (debug) {
                Logger.d(TAG, "drawChildNode:lineW=" + lineW);
            }
            float childNodeY = nodeY + nodeRadius + lineMarginNode + lineLength;
            //绘制节点和子节点的垂直连线
            drawNodeLine(canvas, nodeX, nodeY + nodeRadius + lineMarginNode, nodeX, childNodeY, !taskFlow.isComplete());
            int leftNode;//左边需要绘制的节点数
            if (child.size() % 2 == 0) {
                leftNode = child.size() / 2;
            } else {
                leftNode = child.size() / 2 + 1;
            }
            float childNodeX = nodeX - (leftNode * lineW + leftNode * nodeRadius * 2 - nodeRadius + (leftNode - 1) * lineMarginNode * 2 + lineMarginNode);
            for (int a = 0; a < child.size(); a++) {
                //节点圆
                drawNodeCircle(canvas, childNodeX, childNodeY, !taskFlow.isComplete());
                float nextNodeX = 0;//下个节点的坐标x
                if (a < child.size() - 1) {
                    if (a == leftNode - 1) {
                        nextNodeX += childNodeX + nodeRadius * 2 + lineMarginNode * 2 + lineW * 2;
                    } else {
                        nextNodeX += childNodeX + nodeRadius * 2 + lineMarginNode * 2 + lineW;
                    }
                    //节点连线
                    drawNodeLine(canvas, childNodeX + lineMarginNode + nodeRadius, childNodeY, nextNodeX - lineMarginNode - nodeRadius, childNodeY, !taskFlow.isComplete());
                }

                TaskFlow childNode = child.get(a);
                RectF rect = getNode(childNode).getRect();
                rect.left = childNodeX - nodeRadius;
                rect.top = childNodeY - nodeRadius;
                rect.right = childNodeX + nodeRadius;
                rect.bottom = childNodeY + nodeRadius;

                String name = childNode.getName();
                float baseY = 0;
                float offsetY = 0;
                mTextPaint.setTextAlign(Paint.Align.CENTER);
                mTextPaint.setColor(textColor);

                if (name != null) {
                    mTextPaint.setTextSize(textSize1);
                    mTextPaint.setFakeBoldText(textBold);
                    mTextPaint.getFontMetrics(mFontMetrics);
                    baseY = childNodeY + nodeRadius + childNodeMarginV - mFontMetrics.ascent;
                    offsetY += mFontMetrics.descent - mFontMetrics.ascent;
                    canvas.drawText(name, childNodeX, baseY, mTextPaint);
                    measureChildNodeRect(rect, childNodeX, baseY, mTextPaint.measureText(name), mFontMetrics.bottom);

                }
                String progress = childNode.getProgress();
                if (progress != null) {
                    mTextPaint.setTextSize(textSize3);
                    mTextPaint.setFakeBoldText(false);
                    mTextPaint.getFontMetrics(mFontMetrics);
                    baseY = childNodeY + nodeRadius + childNodeMarginV - mFontMetrics.ascent + offsetY;
                    canvas.drawText(progress, childNodeX, baseY, mTextPaint);
                    offsetY += mFontMetrics.descent - mFontMetrics.ascent;
                    measureChildNodeRect(rect, childNodeX, baseY, mTextPaint.measureText(progress), mFontMetrics.bottom);

                }
                String executor = childNode.getExecutor();
                if (executor != null) {
                    mTextPaint.setTextSize(textSize2);
                    mTextPaint.setFakeBoldText(false);
                    mTextPaint.getFontMetrics(mFontMetrics);
                    baseY = childNodeY + nodeRadius + childNodeMarginV - mFontMetrics.ascent + offsetY;
                    canvas.drawText(executor, childNodeX, baseY, mTextPaint);
                    measureChildNodeRect(rect, childNodeX, baseY, mTextPaint.measureText(executor), mFontMetrics.bottom);

                }
                if (debug) {
                    Logger.d(TAG, "drawChildNode:rect=" + rect);
                }
                childNodeX = nextNodeX;
            }
        }
    }

    private void measureChildNodeRect(RectF rect, float childNodeX, float baseY, float textW, float textBottom) {
        float left = childNodeX - textW / 2;
        float right = childNodeX + textW / 2;
        float bottom = baseY + textBottom;
        if (left < rect.left) {
            rect.left = left;
        }
        if (right > rect.right) {
            rect.right = right;
        }
        if (bottom > rect.bottom) {
            rect.bottom = bottom;
        }
    }


    /**
     * 节点连线
     *
     * @param canvas
     * @param startX
     * @param startY
     * @param endX
     * @param endY
     * @param dashed
     */
    private void drawNodeLine(Canvas canvas, float startX, float startY, float endX, float endY, boolean dashed) {
        if (dashed) {
            mPaint.setPathEffect(mDashPathEffect);
            mPaint.setColor(nodeColor1);
        } else {
            mPaint.setColor(nodeColor2);
        }
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(lineWidth);
        canvas.drawLine(startX, startY, endX, endY, mPaint);
        mPaint.setPathEffect(null);
    }

    /**
     * 绘制节点圆
     *
     * @param canvas
     * @param nodeX
     * @param nodeY
     * @param dashed
     */
    private void drawNodeCircle(Canvas canvas, float nodeX, float nodeY, boolean dashed) {
        if (dashed) {
            mPaint.setColor(nodeColor1);
        } else {
            mPaint.setColor(nodeColor2);
        }
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(nodeX, nodeY, nodeRadius, mPaint);

    }

    /**
     * 数据改变后刷新页面
     */
    public void notifyDataSetChange() {
        requestLayout();
        postInvalidate();

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        childNodeHeight = 0;
        if (mData != null && mData.size() > 0) {
            int size = mData.size();
            int height = getPaddingTop() + getPaddingBottom();
            height += nodeRadius * 2 * size + (lineLength + lineMarginNode * 2) * (size - 1);
            int childNodeCount = getChildNodeCount();
            if (childNodeCount > 0) {
                childNodeHeight = textMarginNode + 2 * childNodeMarginV;
                mTextPaint.setTextSize(textSize1);
                mTextPaint.setFakeBoldText(true);
                mTextPaint.getFontMetrics(mFontMetrics);
                childNodeHeight += mFontMetrics.descent - mFontMetrics.ascent;

                mTextPaint.setTextSize(textSize2);
                mTextPaint.setFakeBoldText(false);
                mTextPaint.getFontMetrics(mFontMetrics);
                childNodeHeight += mFontMetrics.descent - mFontMetrics.ascent;

                mTextPaint.setTextSize(textSize3);
                mTextPaint.setFakeBoldText(false);
                mTextPaint.getFontMetrics(mFontMetrics);
                childNodeHeight += mFontMetrics.descent - mFontMetrics.ascent;
                childNodeHeight += lineLength / 2;
                if (debug) {
                    Logger.d(TAG, "onMeasure:childNodeHeight=" + childNodeHeight);
                }
            }
            height += childNodeHeight * childNodeCount;
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), height);
        } else {
            setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
        }

    }

    /**
     * 子节点的数目
     *
     * @return
     */
    private int getChildNodeCount() {
        int count = 0;
        if (mData != null && mData.size() > 0) {
            for (TaskFlow node : mData) {
                List<TaskFlow> child = node.getChild();
                if (child != null && child.size() > 0) {
                    count++;
                }
            }
        }
        return count;
    }

    /**
     * 获取前面的子节点的数目
     *
     * @return
     */
    private int getBeforeChildNodeCount(int index) {
        int count = 0;
        if (mData != null && mData.size() > 0) {
            for (int i = 0; i < index; i++) {
                List<TaskFlow> child = mData.get(i).getChild();
                if (child != null && child.size() > 0) {
                    count++;
                }

            }
        }
        return count;
    }

    public void setData(List<TaskFlow> data) {
        this.mData = data;
        nodes.clear();
        iconNodes.clear();
        if (mData != null && mData.size() > 0) {
            for (TaskFlow taskFlow : mData) {
                nodes.add(new Node(taskFlow, new RectF()));
                if (taskFlow.getIcon() != null) {
                    iconNodes.add(new Node(taskFlow, new RectF()));
                }
                List<TaskFlow> childs = taskFlow.getChild();
                if (childs != null && childs.size() > 0) {
                    for (TaskFlow child : childs) {
                        nodes.add(new Node(child, new RectF()));
                    }
                }
            }
        }
        if (debug) {
            Logger.d(TAG, "setData:nodes.size=" + nodes.size());
        }
    }

    private Node getNode(TaskFlow taskFlow) {
        if (CollectionUtils.notEmpty(nodes)) {
            for (Node node : nodes) {
                if (node.getTaskFlow() == taskFlow) {
                    return node;
                }
            }
        }
        return null;
    }

    private Node getIconNode(TaskFlow taskFlow) {
        if (CollectionUtils.notEmpty(iconNodes)) {
            for (Node node : iconNodes) {
                if (node.getTaskFlow() == taskFlow) {
                    return node;
                }
            }
        }
        return null;
    }

    private GestureDetector.OnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            float x = e.getX();
            float y = e.getY();
            if (mOnNodeClickListener != null) {
                if (iconNodes != null && iconNodes.size() > 0) {
                    for (Node node : iconNodes) {
                        RectF rect = node.getRect();
                        if (isInBounds(rect, x, y)) {
                            mOnNodeClickListener.onIconClick(node.getTaskFlow());
                            return true;
                        }
                    }
                }

                if (nodes != null && nodes.size() > 0) {
                    for (Node node : nodes) {
                        RectF rect = node.getRect();
                        if (isInBounds(rect, x, y)) {
                            mOnNodeClickListener.onNodeClick(node.getTaskFlow());
                            return true;
                        }

                    }

                }

            }
            return true;
        }
    };

    private boolean isInBounds(RectF rect, float x, float y) {
        if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
            return true;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        boolean b = mGestureDetector.onTouchEvent(event);
        return true;

    }

    public void setOnNodeClickListener(OnNodeClickListener onNodeClickListener) {
        this.mOnNodeClickListener = onNodeClickListener;
    }

    public interface OnNodeClickListener {
        void onNodeClick(TaskFlow taskFlow);

        void onIconClick(TaskFlow taskFlow);
    }

}
